import { PureComponent } from 'react';
import {
  Scene, OrthographicCamera, BoxGeometry, Line, Vector3,
  Mesh, WebGLRenderer, MeshBasicMaterial, ShaderMaterial,
  DirectionalLight, BufferGeometry, BufferAttribute
} from 'three';
import Stats from 'stats.js';
import axios from 'axios';
import { requestAnimationFrame } from '@/utils/requestAnimationFrame';

/**
 * 和原书的931例子有点区别, 这里我使用的是.glsl文件, 而不是书中的.vs .fs, 结果是一样的
 * 以及本地script[type="x-shader/x-vertex"]中书写着色器效果也是一样, 这里只写了从远端请求
 * shader文件的方式, 这就和从远端请求json数据与script[type="application/json"]中写json是一样的
 */

const drawAxes = scene => {
  // 法一:
  const xGeo = new BoxGeometry();
  const xGeoPoints = [
    new Vector3(0, 0, 0),
    new Vector3(6, 0, 0)
  ];
  xGeo.setFromPoints(xGeoPoints);

  // 法二: 这是从其他地方看到的, 于是记录之, 可忽略
  // 创建一个Buffer类型几何体对象
  // const xGeo = new BufferGeometry();
  // // 类型数组创建顶点数据
  // const xVertices = new Int8Array([
  //   0, 0, 0,
  //   6, 0, 0
  // ]);
  // // 创建属性缓冲区对象
  // // 3个为一组，表示一个顶点的xyz坐标
  // const attribute = new BufferAttribute(xVertices, 3);
  // // 设置几何体attributes属性的位置属性
  // xGeo.attributes.position = attribute;

  const xMat = new MeshBasicMaterial({
    color: 0xff0000
  });
  const xAxis = new Line(xGeo, xMat);
  scene.add(xAxis);

  const yGeo = new BoxGeometry();
  const yGeoPoints = [
    new Vector3(0, 0, 0),
    new Vector3(0, 6, 0)
  ];
  yGeo.setFromPoints(yGeoPoints);
  const yMat = new MeshBasicMaterial({
    color: 0x00ff00
  });
  const yAxis = new Line(yGeo, yMat);
  scene.add(yAxis);

  const zGeo = new BoxGeometry();
  const zGeoPoints = [
    new Vector3(0, 0, 0),
    new Vector3(0, 0, 6)
  ];
  zGeo.setFromPoints(zGeoPoints);
  const zMat = new MeshBasicMaterial({
    color: 0x00ccff
  });
  const zAxis = new Line(zGeo, zMat);
  scene.add(zAxis);
};

const scene = new Scene();

drawAxes(scene);

const camera = new OrthographicCamera(-5, 5, 3.75, -3.75, 0.1, 100);
camera.position.set(5, 15, 25);
camera.lookAt(0, 0, 0);

const light = new DirectionalLight();
light.position.set(3, 2, 5);
scene.add(light);

const geometry = new BoxGeometry(2, 2, 2);

const mesh = new Mesh(geometry, null);
scene.add(mesh);

let renderer = null;

let animateId = 0;

const stats = new Stats();

const animate = () => {
  stats.begin();

  mesh.rotation.y += 0.01;

  if(mesh.rotation.y > 2 * Math.PI) {
    mesh.rotation.y -= 2 * Math.PI;
  }

  renderer.render(scene, camera);
  stats.end();
  animateId = requestAnimationFrame(animate);
};

const startAnimate = () => {
  if(animateId) return;
  animateId = requestAnimationFrame(animate);
};

const stopAnimate = () => {
  if(!animateId) return;
  cancelAnimationFrame(animateId);
  animateId = 0;
};

class Index extends PureComponent {

  async componentDidMount() {
    renderer = new WebGLRenderer({
      canvas: document.getElementById('mainCanvas')
    });

    const fpsContainer = document.getElementById('fps-container');
    stats.domElement.style.position = '';
    fpsContainer.appendChild(stats.dom);

    const vertexShaderRes = await axios.get('/shaders/vs.glsl');
    const fragmentShaderRes = await axios.get('/shaders/fs.glsl');
    const { data: vertexShader } = vertexShaderRes;
    const { data: fragmentShader } = fragmentShaderRes;

    mesh.material = new ShaderMaterial({
      vertexShader,
      fragmentShader
    });

    renderer.render(scene, camera);
    
  }

  render() {

    return (
      <div>
        <div>
          <button
            onClick={startAnimate}
          >开始</button>
          <button
            onClick={stopAnimate}
          >停止</button>
        </div>
        <div
          id="fps-container"
          style={{
            display: 'flex',
            padding: '5px'
          }}
        />
        <canvas
          id="mainCanvas"
          width="400"
          height="300"
        />
      </div>
    );
  }
}

export default Index;